package com.liang;

import org.junit.Test;

import java.time.Duration;
import java.time.Instant;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;
import java.util.stream.LongStream;

/**
 * Fork/Join 框架：就是在必要的情况下，将一个大任务，进行拆分(fork)成
 * 若干个小任务（拆到不可再拆时），再将一个个的小任务运算的结果进行 join 汇总。
 *
 *
 * Fork/Join框架与线程池的区别:ForkJoin是ExecutorService的实现类，因此它是一种特殊的线程池
 * 1、 采用 “工作窃取”模式（work-stealing）：当执行新的任务时它可以将其拆分分成更小的任务执行，
 * 并将小任务加到线程队列中，然后再从一个随机线程的队列中偷一个并把它放在自己的队列中。
 *
 * 1、 相对于一般的线程池实现，fork/join框架的优势体现在对其中包含的任务的处理方式上.
 * 在一般的线程池中，如果一个线程正在执行的任务由于某些原因无法继续运行，那么该线程会处于等待状态。
 * 而在fork/join框架实现中，如果某个子问题由于等待另外一个子问题的完成而无法继续运行。那么处理
 * 该子问题的线程会主动寻找其他尚未运行的子问题来执行.这种方式减少了线程的等待时间，提高了性能。
 *
 *
 * @author: Liangxp
 * @date: 2019/8/3 15:38
 */
public class TestForkJoin12 {
    public static void main(String[] args) {
        Instant start = Instant.now();
        ForkJoinPool pool = new ForkJoinPool();
        ForkJoinCalculateSum task = new ForkJoinCalculateSum(0, 10000000000L);

        Long sum = pool.invoke(task);
        System.out.println(sum);

        Instant end = Instant.now();

        System.out.println("耗时：" + Duration.between(start, end).toMillis());

    }

    @Test
    public void test1(){
        Instant start = Instant.now();
        long sum = 0L;
        for (long i = 0L; i <= 10000000000L; i++) {
            sum += i;
        }
        Instant end = Instant.now();
        System.out.println("耗时：" + Duration.between(start, end).toMillis());
    }


    /**
     * java8并行流
     */
    @Test
    public void test2() {
        Instant start = Instant.now();
        long sum = LongStream.rangeClosed(0, 10000000000L)
                .parallel()
                .reduce(0L, Long::sum);

        Instant end = Instant.now();

        System.out.println(Duration.between(start, end).toMillis());
    }
}

class ForkJoinCalculateSum extends RecursiveTask<Long> {

    private long start;
    private long end;

    public ForkJoinCalculateSum(long start, long end) {
        this.start = start;
        this.end = end;
    }

    // 临界值，表示每个小任务，最多只能累加10000个
    private static final long THRESHOLD = 10000L;

    @Override
    protected Long compute() {
        long length = end - start;
        // 当小于临界值THRESHOLD的时候，开始累加任务
        if (length < THRESHOLD) {
            long sum = 0L;
            for (long i = start; i < end; i++) {
                sum += i;
            }
            return sum;
        } else {
            // 任务拆分还没到临界值的时候，继续拆分任务
            long middle = (start + end) / 2;
            ForkJoinCalculateSum left = new ForkJoinCalculateSum(start, middle);
            // 进行拆分，同时压入线程队列
            left.fork();

            ForkJoinCalculateSum right = new ForkJoinCalculateSum(middle + 1, end);
            right.fork();

            return left.join() + right.join();
        }
    }
}
